#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "ws-server-cmd-sch.h"
#include "gr5513_ota_m.h"

#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10))

#define EIR_FLAGS                   0X01
#define EIR_NAME_SHORT              0x08
#define EIR_NAME_COMPLETE           0x09
#define EIR_MANUFACTURE_SPECIFIC    0xFF
#define ADV_NONCONN_IND             0x03

#define location					0x02
#define cmd  						0xAD
#define par1  						0x0A
#define par2  						0x25

int global_done = 0;


#define BT_HCI_EVT_LE_EXT_ADV_REPORT	0x0d
struct bt_hci_evt_le_ext_adv_report {
	uint8_t  num_reports;
} __attribute__ ((packed));
struct bt_hci_le_ext_adv_report {
	uint8_t  event_type;
	uint8_t  addr_type;
	uint8_t  addr[6];
} __attribute__ ((packed));

static volatile int scan_signal_received = 0;

static void scan_sigint_handler(int sig)
{
	scan_signal_received = sig;
	sig = 0;
}

static int nuba2str(const bdaddr_t *ba, char *str)
{
	return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
				   ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
}

static int print_advertising_devices(int dd, uint8_t filter_type, unsigned int seconds)
{
	unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
	struct hci_filter nf, of;
	struct sigaction sa;
	socklen_t olen;
	int len;
    uint16_t counter_num = 0;
	len = 0;

	olen = sizeof(of);
	if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0)
	{
		printf("Could not get socket options\n");
		return -1;
	}

	hci_filter_clear(&nf);
	hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
	hci_filter_set_event(EVT_LE_META_EVENT, &nf);

	if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
	{
		printf("Could not set socket options\n");
		return -1;
	}

	memset(&sa, 0, sizeof(sa));
	sa.sa_flags = SA_NOCLDSTOP;
	sa.sa_handler = scan_sigint_handler;
	sigaction(SIGINT, &sa, NULL);
	sigaction(SIGALRM, &sa, NULL);
	alarm(seconds);
    counter_num = 0;
	while (1)
	{
		const struct bt_hci_le_ext_adv_report  *info;
		

		char addr[100];
		char data_xd[128];

		while ((len = read(dd, buf, sizeof(buf))) < 0)
		{
			if ((errno == EINTR && scan_signal_received == SIGINT) || scan_signal_received == SIGALRM)
			{
				len = 0;
				goto done;
			}

			if (errno == EAGAIN || errno == EINTR)
				continue;
			goto done;
		}

		if (scan_signal_received == SIGALRM)
		{
			printf("SIGALRM \n");
			scan_signal_received = 0;
			goto done;
		}
		ptr = buf + 5;
		len -= 5;

		info = (void *)ptr;

		if (buf[4] != 0x01)
			goto done;

		/* Ignoring multiple reports */
		switch (info->event_type)
		{
		case 0x00:
			goto printf_data;
			break;
		case 0x01:
			goto printf_data;
			break;
		case 0x02:
			goto printf_data;
			break;
		case 0x03:
			goto printf_data;;
			break;
		case 0x1a:
			goto printf_data;
			break;
		case 0x1b:
			goto printf_data;
			break;
		default:
			goto data_null;
		}
printf_data:
			if((int8_t)ptr[len-1] < -70) goto data_null; //RSSI 小于 -60 过滤
            nuba2str(&info->addr, addr);
			if(addr[0] !='1' && addr[0] !='D') goto data_null; // mac第一位地址过滤
			sprintf(data_xd,"RSSI,%d,",(int8_t)ptr[len-1]);
			strcat(data_xd,addr);
			//fputs(data_xd, fd);
			printf("%d RSSI =%d  MAC = %s", counter_num , (int8_t)ptr[len-1], addr);
			send_data_to_ws(data_xd,strlen(data_xd),0);
            counter_num++;

	data_null:
    memset(buf,0,HCI_MAX_EVENT_SIZE);
	}

done:
	setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
	if (len < 0)
		return -1;

	return 0;
}

void ble_scan(void *arg)
{
	inquiry_info *ii = NULL;
	int err;
	int dev_id, sock;
	bdaddr_t addr = {{0xEA,0xCB,0x3E,0xCF,0x00,0x0F}};

	uint8_t own_type = LE_RANDOM_ADDRESS;
	uint8_t scan_type = 0x01;
	uint8_t filter_type = 1;
	uint8_t filter_policy = 0;
	uint16_t interval = htobs(0x0024);
	uint16_t window = htobs(0x0024);
	uint8_t filter_dup = 0x00;

    printids(__func__);
	dev_id = hci_get_route(NULL);
	printf("hci_get_route = %d\n",dev_id);
	sock = hci_open_dev(dev_id);

	if (dev_id < 0 || sock < 0)
	{
		perror("opening socket");
		exit(1);
	}

	err = hci_le_set_scan_parameters(sock, scan_type, interval, window,
									 own_type, filter_policy, 10000);
	if (err < 0)
	{
		perror("Set scan parameters failed");
		exit(1);
	}

	err = hci_le_set_scan_enable(sock, 0x01, filter_dup, 10000);
	if (err < 0)
	{
		perror("Enable scan failed");
		exit(1);
	}

	printf("LE Scan ...\n");

	err = print_advertising_devices(sock, filter_type, 10);
	if (err < 0)
	{
		perror("Could not receive advertising events");
		exit(1);
	}

	printf("Disable scan ...\n");

	err = hci_le_set_scan_enable(sock, 0x00, filter_dup, 10000);
	if (err < 0)
	{
		perror("Disable scan failed");
		exit(1);
	}
	printf("scan exit \n");
	hci_close_dev(sock);
	free(ii);
	close(sock);
	return 0;
}

unsigned int *uuid_str_to_data(char *uuid)
{
	char conv[] = "0123456789ABCDEF";
	int len = strlen(uuid);
	unsigned int *data = (unsigned int *)malloc(sizeof(unsigned int) * len);
	unsigned int *dp = data;
	char *cu = uuid;

	for (; cu < uuid + len; dp++, cu += 2)
	{
		*dp = ((strchr(conv, toupper(*cu)) - conv) * 16) + (strchr(conv, toupper(*(cu + 1))) - conv);
	}

	return data;
}

unsigned int twoc(int in, int t)
{
	return (in < 0) ? (in + (2 << (t - 1))) : in;
}

int enable_advertising(int advertising_interval, char *advertising_uuid, int major_number, int minor_number, int rssi_value)
{
	int device_id = hci_get_route(NULL);

	int device_handle = 0;
	if ((device_handle = hci_open_dev(device_id)) < 0)
	{
		perror("Could not open device");
		exit(1);
	}

	le_set_advertising_parameters_cp adv_params_cp;
	memset(&adv_params_cp, 0, sizeof(adv_params_cp));
	adv_params_cp.min_interval = htobs(advertising_interval);
	adv_params_cp.max_interval = htobs(advertising_interval);
	adv_params_cp.chan_map = 7;
	adv_params_cp.advtype = ADV_NONCONN_IND;

	uint8_t status;
	struct hci_request rq;
	memset(&rq, 0, sizeof(rq));
	rq.ogf = OGF_LE_CTL;
	rq.ocf = OCF_LE_SET_ADVERTISING_PARAMETERS;
	rq.cparam = &adv_params_cp;
	rq.clen = LE_SET_ADVERTISING_PARAMETERS_CP_SIZE;
	rq.rparam = &status;
	rq.rlen = 1;

	int ret = hci_send_req(device_handle, &rq, 1000);
	if (ret < 0)
	{
		hci_close_dev(device_handle);
		fprintf(stderr, "Can't send request %s (%d)\n", strerror(errno), errno);
		return (1);
	}

	le_set_advertise_enable_cp advertise_cp;
	memset(&advertise_cp, 0, sizeof(advertise_cp));
	advertise_cp.enable = 0x01;

	memset(&rq, 0, sizeof(rq));
	rq.ogf = OGF_LE_CTL;
	rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
	rq.cparam = &advertise_cp;
	rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
	rq.rparam = &status;
	rq.rlen = 1;

	ret = hci_send_req(device_handle, &rq, 1000);

	if (ret < 0)
	{
		hci_close_dev(device_handle);
		fprintf(stderr, "Can't send request %s (%d)\n", strerror(errno), errno);
		return (1);
	}

	le_set_advertising_data_cp adv_data_cp;
	memset(&adv_data_cp, 0, sizeof(adv_data_cp));

	uint8_t segment_length = 1;
#ifdef ibeacon
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(EIR_FLAGS);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x1A);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length] = htobs(segment_length - 1);

	adv_data_cp.length += segment_length;

	segment_length = 1;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(EIR_MANUFACTURE_SPECIFIC);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x4C);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x00);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x02);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x15);
	segment_length++;

	unsigned int *uuid = uuid_str_to_data(advertising_uuid);
	int i;
	for (i = 0; i < strlen(advertising_uuid) / 2; i++)
	{
		adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(uuid[i]);
		segment_length++;
	}

	// Major number
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(major_number >> 8 & 0x00FF);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(major_number & 0x00FF);
	segment_length++;

	// Minor number
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(minor_number >> 8 & 0x00FF);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(minor_number & 0x00FF);
	segment_length++;

	// RSSI calibration
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(twoc(rssi_value, 8));
	segment_length++;

	adv_data_cp.data[adv_data_cp.length] = htobs(segment_length - 1);

	adv_data_cp.length += segment_length;
#else
	//hcitool -i hci0 cmd 0x08 0x0008 0D 0C FF 59 53 %02x %02x %02x 00  03 00 %02x 31 32\n",location,cmd,par1,par2);
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(EIR_MANUFACTURE_SPECIFIC);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x59);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x53);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(location);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(cmd);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(par1);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x00);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x03);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(0x00);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs(par2);
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs('1');
	segment_length++;
	adv_data_cp.data[adv_data_cp.length + segment_length] = htobs('2');
	segment_length++;

	adv_data_cp.data[adv_data_cp.length] = htobs(segment_length - 1);
	adv_data_cp.length += segment_length;
#endif // DEBUG

	memset(&rq, 0, sizeof(rq));
	rq.ogf = OGF_LE_CTL;
	rq.ocf = OCF_LE_SET_ADVERTISING_DATA;
	rq.cparam = &adv_data_cp;
	rq.clen = LE_SET_ADVERTISING_DATA_CP_SIZE;
	rq.rparam = &status;
	rq.rlen = 1;

	ret = hci_send_req(device_handle, &rq, 1000);

	hci_close_dev(device_handle);

	if (ret < 0)
	{
		fprintf(stderr, "Can't send request %s (%d)\n", strerror(errno), errno);
		return (1);
	}

	if (status)
	{
		fprintf(stderr, "LE set advertise returned status %d\n", status);
		return (1);
	}
}

int disable_advertising()
{
	int device_id = hci_get_route(NULL);

	int device_handle = 0;
	if ((device_handle = hci_open_dev(device_id)) < 0)
	{
		perror("Could not open device");
		return (1);
	}

	le_set_advertise_enable_cp advertise_cp;
	uint8_t status;

	memset(&advertise_cp, 0, sizeof(advertise_cp));

	struct hci_request rq;
	memset(&rq, 0, sizeof(rq));
	rq.ogf = OGF_LE_CTL;
	rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
	rq.cparam = &advertise_cp;
	rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
	rq.rparam = &status;
	rq.rlen = 1;

	int ret = hci_send_req(device_handle, &rq, 1000);

	hci_close_dev(device_handle);

	if (ret < 0)
	{
		fprintf(stderr, "Can't set advertise mode: %s (%d)\n", strerror(errno), errno);
		return (1);
	}

	if (status)
	{
		fprintf(stderr, "LE set advertise enable on returned status %d\n", status);
		return (1);
	}
}

void adv_ctrlc_handler(int s)
{
	global_done = 1;
	fprintf(stderr, "adv_ctrlc_handler %d\n", s);
}

void ble_adv(uint16_t overtime)
{
	global_done = 0;
	int rc = enable_advertising(200, '0', 0, 0,0);
	if (rc == 0)
	{
		struct sigaction sigint_handler;

		sigint_handler.sa_handler = adv_ctrlc_handler;
		sigemptyset(&sigint_handler.sa_mask);
		sigint_handler.sa_flags = 0;

		sigaction(SIGALRM, &sigint_handler, NULL);
		alarm(overtime);
		fprintf(stderr, "wait alarm(%ds) stop advertising\n",overtime);

		while (!global_done)
		{
			sleep(1);
		}

		fprintf(stderr, "Shutting down\n");
		disable_advertising();
		return;
	}
	fprintf(stderr, "ble_adv run err\n");
}
